package jfrederick8;

import java.io.File;
import java.io.FileNotFoundException;
import java.util.LinkedList;
import java.util.Random;
import java.util.Scanner;

/**
 * This class represents the field of play on which the StoryCharacters move and interact. It is made up of the individual tiles in which items and StoryCharacters may be contained
 * 
 * @author John Frederick
 * @version 1.0 - 11 February 2012
 */
public class Field{
	/** the number of tiles which will exist on the field of play in the horizontal direction */
	private int width;
	/** the number of tiles which will exist on the field of play in the vertical direction */
	private int height;
	/** the list of all of the tiles within the field of play */
	private Tile[][] tileList;
	/** the tiles which are set to display with blue overlay to indicate the ability of a StoryCharacter to move into those tiles */
	private int[][] overlay;
	/** the randomization object for any randomization algorithms */
	private Random rand;
	/** used to determine the cap on the random integer throw */
	private int randCap;
	/** list of visited Tiles used in dijkstra's algorithm */
	private LinkedList<Tile> visited;
	/** orders the tiles for dijkstra's algorithm */
	private LinkedList<Tile> queue;
	/** this file tells field the type of tiles to create for each level */
	private File level;
	/** scanner for reading in level information */
	private Scanner scan;
	/** the array of chars within each scanned line of text */
	private char[] chars;
	/** tracks the lines which have been scanned */
	private int lineTracker;
	
	/**
	 * This constructs the set of tiles on which the StoryCharacters will play
	 * 
	 * @param width the number of horizontal tiles to create for the field of play
	 * @param height the number of vertical tiles to create for the field of play
	 */
	public Field(int width, int height, File levelText){
		//TODO:set field tiles (read in?), set width/height
		this.level = levelText;
		rand = new Random();
		visited = new LinkedList<Tile>();
		queue = new LinkedList<Tile>();
		this.width = width;
		this.height = height;
		tileList = new Tile[width][height];
		overlay = new int[width][height];
		readInTiles();
	}//end constructor Field
	
	/**
	 * sets all of the tiles to false so that the overlay will not appear over them
	 */
	public void setOverlayNull(){
		for(int i=0; i < overlay.length;i++){
			for(int j=0; j < overlay[0].length; j++){
				overlay[i][j] = 0;
				tileList[i][j].setAlterableWeight(100);
			}
		}
	}//end method setOverlay Null
	
	/**
	 * This method acts as a helper method for setOverlay, which measures the weights for entering into the adjacent spaces.
	 * 
	 * @param range the distance from the origin which should be overlayed
	 */
	private void dijkstrasHelp(int range){
		Tile tile = queue.get(0);
		if (tile.getX() + 1 < tileList.length){
			if(visited.contains(tileList[tile.getX() + 1][tile.getY()]) == false){
				if(tile.getAlterableWeight() + tileList[tile.getX() + 1][tile.getY()].getWalkWeight() < range){
					queue.add(tileList[tile.getX() + 1][tile.getY()]);
					tileList[tile.getX() + 1][tile.getY()].setAlterableWeight(tile.getAlterableWeight() + tileList[tile.getX() + 1][tile.getY()].getWalkWeight());
					overlay[tile.getX() + 1][tile.getY()] = 1;
				}
				visited.add(tileList[tile.getX() + 1][tile.getY()]);
			}
		}
		if (tile.getX() - 1 >= 0){
			if(visited.contains(tileList[tile.getX() - 1][tile.getY()]) == false){
				if(tile.getAlterableWeight() + tileList[tile.getX() - 1][tile.getY()].getWalkWeight() < range){
					queue.add(tileList[tile.getX() - 1][tile.getY()]);
					tileList[tile.getX() - 1][tile.getY()].setAlterableWeight(tile.getAlterableWeight() + tileList[tile.getX() - 1][tile.getY()].getWalkWeight());
					overlay[tile.getX() - 1][tile.getY()] = 1;
				}
				visited.add(tileList[tile.getX() - 1][tile.getY()]);
			}
		}
		if (tile.getY() + 1 < tileList[0].length){
			if(visited.contains(tileList[tile.getX()][tile.getY() + 1]) == false){
				if(tile.getAlterableWeight() + tileList[tile.getX()][tile.getY() + 1].getWalkWeight() < range){
					queue.add(tileList[tile.getX()][tile.getY() + 1]);
					tileList[tile.getX()][tile.getY() + 1].setAlterableWeight(tile.getAlterableWeight() + tileList[tile.getX()][tile.getY() + 1].getWalkWeight());
					overlay[tile.getX()][tile.getY() + 1] = 1;
				}
				visited.add(tileList[tile.getX()][tile.getY() + 1]);
			}
		}
		if (tile.getY() - 1 >= 0){
			if(visited.contains(tileList[tile.getX()][tile.getY() - 1]) == false){
				if(tile.getAlterableWeight() + tileList[tile.getX()][tile.getY() - 1].getWalkWeight() < range){
					queue.add(tileList[tile.getX()][tile.getY() - 1]);
					tileList[tile.getX()][tile.getY() - 1].setAlterableWeight(tile.getAlterableWeight() + tileList[tile.getX()][tile.getY() - 1].getWalkWeight());
					overlay[tile.getX()][tile.getY() - 1] = 1;
				}
				visited.add(tileList[tile.getX()][tile.getY() - 1]);
			}
		}
	}//end method dijkstraHelp
	
	/**
	 * sets the specified tiles to be drawn with a blue overlay
	 * 
	 * @param pattern the algorithm which the method should use to determine the correct tiles to overlay
	 * @param range the distance which the overlay will span
	 * @param x the origin of the overlay (horizontal)
	 * @param y the origin of the overlay (vertical)
	 */
	public void setOverlay(int pattern, int range, int x, int y){
		switch(pattern){
		case 0: 
			visited.clear();
			queue.clear();
			tileList[x][y].setAlterableWeight(0);
			visited.add(tileList[x][y]);
			queue.add(tileList[x][y]);
			while (queue.isEmpty() == false){
				dijkstrasHelp(range);
				queue.remove();
			}
			break;
		}
	}//end method setOverlay
	
	/**
	 * A chained method which defaults the overlay algorithm to a radiating overlay
	 * 
	 * @param range the distance which the overlay will span.
	 * @param x the origin of the overlay (horizontal)
	 * @param y the origin of the overlay (vertical)
	 */
	public void setOverlay(int range, int x, int y){
		setOverlay(0,range,x,y);
	}//end method setOverlay
	
	/**
	 * This method takes the level File and creates the corresponding tiles based off of the level text content.
	 */
	private void readInTiles(){
		try {
			scan = new Scanner(level);
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			System.out.println("Level text not found");
			System.exit(0);
		}
		lineTracker = 0;
		while(lineTracker<tileList[0].length){
			chars = scan.nextLine().toCharArray();
			for (int i=0;i<tileList.length;i++){
				tileList[i][lineTracker] = new Tile(i,lineTracker,chars[i] - 48);
			}
			lineTracker++;
		}
	}//end method readInTiles
	
	/**
	 * This class assigns the tiles with their types according to a specified randomization algorithm
	 */
	private void generateTiles(){
		//TODO:alter random generation
		for (int i=0;i<width;i++){
			for (int j=0;j<height;j++){
				randCap = 20;
				if (i>0){
					if (tileList[i-1][j].getType() == 2){
						randCap = 2;
					}
				} else if (j>0){
					if (tileList[i][j - 1].getType() == 2){
						randCap = 2;
					}
				}
				if (rand.nextInt(randCap) == 0){
					tileList[i][j] = new Tile(i,j,2);
				} else {
					tileList[i][j] = new Tile(i,j,0);
				}
			}
		}
	}
	
	/**
	 * This allows outside access to the tiles in the field of play
	 * 
	 * @return the list of the field's tiles
	 */
	public Tile[][] getTiles(){
		return tileList;
	}//end getter getTiles
	
	/**
	 * allows access to the overlay list so that the overlay may be drawn
	 * 
	 * @return the overlay lists
	 */
	public int[][] getOverlay(){
		return overlay;
	}//end getter getOverlay
	
	/**
	 * This allows access to the size of the field
	 * 
	 * @return the number of horizontal tiles which are part of the field
	 */
	public int getWidth(){
		return width;
	}//end getter getWidth
	
	/**
	 * This allows access to the size of the field
	 * 
	 * @return the number of vertical tiles which are part of the field
	 */
	public int getHeight(){
		return height;
	}//end getter getHeight
}//end class Field
